<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>JSDoc: Source: drs.js</title>

    <script src="scripts/prettify/prettify.js"> </script>
    <script src="scripts/prettify/lang-css.js"> </script>
    <!--[if lt IE 9]>
      <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
    <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>

<body>

<div id="main">

    <h1 class="page-title">Source: drs.js</h1>

    



    
    <section>
        <article>
            <pre class="prettyprint source linenums"><code>/**
 * @namespace core
**/
var core = core || {};

/**
 * @memberof core
 * @namespace core.util
**/
core.util = core.util || {}

/**
 * @memberof core.util
 * @namespace core.util.DRS
 * @description All DRS methods are available under this namespace.
 *
 * @version 0.1.1
 * @author Simplex Studio, LTD &amp;
 * @author original source: http://codepen.io/zz85/post/resizing-moving-snapping-windows-with-js-css, https://github.com/zz85
 *
 * @licence The MIT License (MIT)
 * @Copyright Copyright © 2015 Simplex Studio, LTD
 * @Copyright Copyright © 2015 https://github.com/zz85
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the “Software”), to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
 * of the Software.
 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
core.util.DRS = function(){

/**
 * @memberof core.util.DRS
 * @class
 * @classdesc Adds Drag Resize &amp; Snap to any element.
 *
 *
 * @param pane {HTMLElement} 	- The element to add drag, resize, snap.
 * @param handle {HTMLElement | object | string | Array } - A single handle or Array of handles.
 * 				 						HTMLElement :Any HTMLElement to use and a drag handle.  Any elements
 * 				 									 with class "drag-area" will be added automatically.
 *				 						String: Such as "top" see getHandleCoords() for more presets.
 * 				 						Object: An object representing the bounds of the handle.
 * @param options {object}				- An object specifying available options.
 * @param options.snapEdge {number}		- (5) qty pixels pane edge needs to be beyond window to snap.
 * @param options.snapFull {number}		- (100) qty pixels pane edge needs to be beyond window to snap full screen.
 * @param options.resizeOuterM {number}	- (5) qty pixels outside pane bounds is dragable.
 * @param options.resizeInnerM {number}	- (8) qty pixels inside pane bounds is dragable.
 *
 * @example &lt;caption>Basic usage:&lt;/caption>
 * var pane = document.getElementById('pane')
 * var drs = core.util.DRS.makeDRS(pane)
 *
 * @example &lt;caption>Advanced usage:&lt;/caption>
 * // Get an Create an element to drag, resize, and snap
 * var pane = document.getElementById('pane')
 *
 * // (optional) Create an HTMLElement to use as a drag handle.
 * // NOTE: Any child element of pane with class 'drag-area' will be added automatically.
 * var handleOne = document.getElementById('some-id')
 *
 * // (optional) create a drag handle by absolutely positioned css rules (bounds).
 * // Supply 4 sides or three sides and width or height to make four bounds.
 * var handleTwo = {
 * 	left:0, 			// style.left
 * 	bottom:28, 			// style.bottom
 * 	top:0, 				// style.tip
 * 	width:33, 			// style.width
 * 	right:null, 		// style.right
 * 	height:null, 		// style.height
 * 	invert: false,		// (false) true: Will invert the bounding box (areas not covered are dragable)
 * 	hide: false,   		// (false) true: Will not append and HTMLelement for the area defined
 *}
 * // Specify options
 * var options = {
 * 	snapEdge: 5, 		// (5) qty pixels pane needs to be moved beyond window to snap
 * 	snapFull: 100, 	 	// (100) qty pixels pane needs to be moved beyond window to snap full screen
 * 	resizeOuterM: 5, 	// (5) qty pixels outside pane bounds is dragable
 * 	resizeInnerM: 8,   // (8) qty pixels inside pane bounds is dragable
 * }
 *
 * // Apply the drag resize snap to your element
 * var drs = core.util.DRS.makeDRS(pane, [handleOne , handleTwo], options)
 *
 * @returns DRS {object}
 *
 */
var makeDRS = function(pane, handle, options){
	"use strict";

    options = options || {}

	// Minimum resizable area
	var minWidth 			= 60;
	var minHeight			= 40;

	// Thresholds
	var SNAP_MARGINS 		= -(options.snapEdge || 5);
	var SNAP_FS_MARGINS 	= -(options.snapFull || 100);
	var RESIZE_MARGIN_INNER = options.resizeOuterM || 5;
	var RESIZE_MARGIN_OUTER = options.resizeOuterM || 8;


	// End of what's configurable.
	var onRightEdge, onBottomEdge, onLeftEdge, onTopEdge;
	var rightScreenEdge, bottomScreenEdge, topScreenEdge, leftScreenEdge;
	var e, b, x, y, inLR, inTB, preSnap;
	var clicked 			= null;
	var redraw 				= false;
	var _usePercent 		= false  // Sets the init state of pane bounds type "%" of screen or fixed "px"

	// make sure pane has some required styles
    pane.style.boxSizing = "border-box" // make sure that bounds take border into account


	// create ghostpane
	var ghostpane = document.createElement('div')
		ghostpane.id = "DRSghost"
		ghostpane.style.opacity = "0"
		document.body.appendChild(ghostpane)

	// Setup drag handles
	var handles = handle instanceof Array ?  handle : [handle]
	var onHTMLhandle 		// Holds result of event listeners for HTML handles

	function createHandleObjects() {
		b = b || pane.getBoundingClientRect();

		// Grab handles from dom if none are supplied
		if (!hasHTMLElement(handles))
			handles = handles.concat(
				[].slice.call(
					pane.getElementsByClassName('drag-area')))

		// precess handles
		for (var i in handles){
			// html
			if (handles[i] instanceof HTMLElement) {
				bindEvents(handles[i], 'mousedown touchstart', handleDown)
				bindEvents(handles[i], 'mouseup touchend', handleUp)
				handles[i] = {ele: handles[i], type: 'html'}

			// bounds object
			}else if (handles[i] instanceof Object) {
				handles[i] = {type:'custom', coords:handles[i]}
				drawDragHandle(handles[i])

			// preset strings
			}else{
				handles[i] = {type:handles[i]}
				drawDragHandle(handles[i])
			}
		}
	}

	window.addEventListener('load', createHandleObjects())

	function handleDown(){onHTMLhandle = true}

	function handleUp(){onHTMLhandle = false}

	// core functions

	function setBounds(element, x, y, w, h) {
		if (x === undefined) {
			b = b || pane.getBoundingClientRect();
			x = b.left
			y = b.top
			w = b.width
			h = b.height
		}
		var wh = convertUnits(w, h)
		element.style.left = x + 'px';
		element.style.top = y + 'px';
		element.style.width = wh[0];
		element.style.height = wh[1];
	}

	function getBounds(){
		var winW = window.innerWidth
	    var winH = window.innerHeight
		var bounds = []
		if (b.top &lt; SNAP_FS_MARGINS || b.left &lt; SNAP_FS_MARGINS ||
			b.right > winW - SNAP_FS_MARGINS || b.bottom > winH - SNAP_FS_MARGINS) {
		  bounds = [0, 0, winW, winH]
		} else if (leftScreenEdge) {
		  bounds = [0, 0, winW / 2, winH]
		} else if (rightScreenEdge) {
		  bounds = [winW / 2, 0, winW / 2, winH]
		} else if (topScreenEdge) {
		  bounds = [0, 0, winW, winH / 2]
		} else if (bottomScreenEdge) {
		  bounds = [0, winH / 2, winW, winH / 2]
		}
		return bounds
	}

	function convertUnits(w, h){
		if(!_usePercent)
			return [w + 'px', h + 'px']
		var pH, pW
		// use docWidth to take scroll bars into account!
		var docWidth = document.documentElement.clientWidth || document.body.clientWidth;
		pH = h / window.innerHeight * 100
		pW = w / docWidth * 100

		var r = [pW + '\%', pH + '\%']
		return r
	}

	/**
	 * Toggle pane size as percent of window or fixed size
	 * @param state {boolean} true/false
	 */
	function togglePercent(state){
		_usePercent = state !== undefined? state : !_usePercent
		setBounds(pane)
	}

	/**
	 * Toggle full screen mode
	 */
	function snapFullScreen(){
		preSnap = {width: b.width, height: b.height, top:b.top, left:b.left};
		preSnap.to = [pane, 0, 0, window.innerWidth, window.innerHeight]
		setBounds.apply(this, preSnap.to);
	}

	/**
	 * Restore pre-snap size and position
	 */
	function restorePreSnap() {
		if(!preSnap) return
		var p = preSnap
		setBounds(pane, p.left, p.top, p.width, p.height);
	}

	function hintHide() {
	  setBounds(ghostpane, b.left, b.top, b.width, b.height);
	  ghostpane.style.opacity = 0;
	}

	// Mouse events
	document.addEventListener('mousedown', onMouseDown);
	document.addEventListener('mousemove', onMove);
	document.addEventListener('mouseup', onUp);

	// Touch events
	document.addEventListener('touchstart', onTouchDown);
	document.addEventListener('touchmove', onTouchMove);
	document.addEventListener('touchend', onTouchEnd);

	function onTouchDown(e) {
	  onDown(e.touches[0]);
	  //e.preventDefault();
	}

	function onTouchMove(e) {
	  onMove(e.touches[0]);
	  if (clicked &amp;&amp; (clicked.isMoving || clicked.isResizing)) {
		  e.preventDefault()
		  e.stopPropagation()
	  }
	}

	function onTouchEnd(e) {
	  if (e.touches.length ==0) onUp(e.changedTouches[0]);
	}

	function onMouseDown(e) {
	  onDown(e);
	}

	function onDown(e) {

		calc(e);
		var isResizing = onRightEdge || onBottomEdge || onTopEdge || onLeftEdge;

		clicked = {
			x: x,
			y: y,
			cx: e.clientX,
			cy: e.clientY,
			w: b.width,
			h: b.height,
			isResizing: isResizing,
			isMoving: !isResizing &amp;&amp; canMove(),
			onTopEdge: onTopEdge,
			onLeftEdge: onLeftEdge,
			onRightEdge: onRightEdge,
			onBottomEdge: onBottomEdge
		};
	}

	function canMove() {
		for (var i in handles) {
			var h = drawDragHandle(handles[i])
			var c = h.coords
			var r = {}
			var yb = b.height - y
			var xr = b.width - x

			// determine bounds of click area coordinates
			if (c.bottom !== null &amp;&amp; c.bottom !== undefined)
				r.bottom = yb &lt; c.height || (!c.height &amp;&amp; y > c.top) &amp;&amp; yb > c.bottom &amp;&amp; inLR
			if (c.top !== null &amp;&amp; c.top !== undefined)
				r.top = y &lt; c.height || (!c.height &amp;&amp; yb > c.bottom) &amp;&amp; y > c.top &amp;&amp; inLR
			if (c.right !== null &amp;&amp; c.right !== undefined)
				r.right = xr &lt; c.width || (!c.width &amp;&amp; x > c.left ) &amp;&amp; xr > c.right &amp;&amp; inTB
			if (c.left !== null &amp;&amp; c.left !== undefined)
				r.left = x &lt; c.width || (!c.width &amp;&amp; xr > c.right ) &amp;&amp; x > c.left &amp;&amp; inTB

			var result = ( (r.bottom || r.top  ) &amp;&amp; r.left &amp;&amp; r.right ) ||
				( (r.left || r.right) &amp;&amp; r.bottom &amp;&amp; r.top   )

			if (c.invert &amp;&amp; !result || onHTMLhandle) return true
			else if( result || onHTMLhandle ) return true
		}
		return false
	}

	function drawDragHandle(h){
		var c = getHandleCoords(h)
		if (h.type == 'html') return h
		if (h.coords.hide) return h
		if (!h.drawn) {
			var e = document.createElement('div')
			e.className = 'drag-area'
			e.style.position = "absolute"
			e.style.pointerEvents = "all"
			e.style.overflow = "hidden"
			pane.appendChild(e)
			h.drawn = true
			h.ele = e
		}

		if( c.bottom  	!== null )	h.ele.style.bottom 	= 	c.bottom 	+ "px"
		if( c.right		!== null )	h.ele.style.right 	= 	c.right 	+ "px"
		if( c.top		!== null )	h.ele.style.top 	= 	c.top 		+ "px"
		if( c.left 		!== null )	h.ele.style.left 	= 	c.left 		+ "px"
		if( c.height 	!== null )	h.ele.style.height 	= 	c.height	+ "px"
        if( c.width 	!== null )	h.ele.style.width 	= 	c.width		+ "px"
		return h
    }

	function getHandleCoords(h){
	  var DO = 30
	  var types = {
		  top: 		{top:0, 	bottom:null,	left:0, 	right:0, 	width:null, height:DO, 		invert:false},
		  bottom: 	{top:null, 	bottom:0, 		left:0, 	right:0, 	width:null, height:DO, 		invert:false},
		  left: 	{top:0, 	bottom:0, 		left:0, 	right:null, width:DO, 	height:null, 	invert:false},
		  right: 	{top:0, 	bottom:0, 		left:null, 	right:0, 	width:DO, 	height:null, 	invert:false},
		  full: 	{top:0, 	bottom:0, 		left:0, 	right:0, 	width:null, height:null, 	invert:false},
		  20:  		{top:20, 	bottom:20, 		left:20, 	right:20, 	width:null, height:null, 	invert:false},
		  none:		{top:null, 	bottom:null,	left:null, 	right:null, width:null, height:null, 	invert:false}
	  }
	  if (h.type instanceof Array)
		  h.coords = h.type
	  else if(h.type == 'html')
		  h.coords = h.ele.getBoundingClientRect()
	  else if (!h.type)
	  	  h.type = 'full'

	  if (!h.coords){
		  h.coords = types[h.type.split(' ')[0]]
		  h.coords.invert = h.type.indexOf('invert')+1
		  h.coords.hide = h.type.indexOf('hide')+1
	  }
	  return h.coords
	}

	function calc(e) {
		b = pane.getBoundingClientRect();
		x = e.clientX - b.left;
		y = e.clientY - b.top;

		// define inner and outer margins
		var dMi = RESIZE_MARGIN_INNER
		var dMo = -RESIZE_MARGIN_OUTER
		var rMi = b.width - RESIZE_MARGIN_INNER
		var rMo = b.width + RESIZE_MARGIN_OUTER
		var bMi = b.height - RESIZE_MARGIN_INNER
		var bMo = b.height + RESIZE_MARGIN_OUTER
		inLR = x > dMo &amp;&amp; x &lt; rMo
		inTB = y > dMo &amp;&amp; y &lt; bMo

		onTopEdge 		= y &lt;= dMi 	&amp;&amp; y > dMo &amp;&amp; inLR;
		onLeftEdge 		= x &lt;= dMi 	&amp;&amp; x > dMo &amp;&amp; inTB;
		onBottomEdge 	= y >= bMi 	&amp;&amp; y &lt; bMo &amp;&amp; inLR;
		onRightEdge 	= x >= rMi	&amp;&amp; x &lt; rMo &amp;&amp; inTB;

		rightScreenEdge 	= b.right > window.innerWidth - SNAP_MARGINS;
		bottomScreenEdge 	= b.bottom > window.innerHeight - SNAP_MARGINS;
		topScreenEdge 		= b.top &lt; SNAP_MARGINS;
		leftScreenEdge 		= b.left &lt; SNAP_MARGINS;
	}

	function onMove(ee) {
		calc(ee);
		e = ee;
		redraw = true;
	}

	function animate() {

		requestAnimationFrame(animate);

		if (!redraw) return;

		redraw = false;

		// handle resizeing
		if (clicked &amp;&amp; clicked.isResizing) {
			if (clicked.onRightEdge)
				pane.style.width = Math.max(x, minWidth) + "px";
			if (clicked.onBottomEdge)
				pane.style.height = Math.max(y, minHeight) + "px";
			if (clicked.onLeftEdge) {
				var currentWidth = Math.max(clicked.cx - e.clientX + clicked.w, minWidth);
				if (currentWidth > minWidth) {
					pane.style.width = currentWidth + "px";
					pane.style.left = e.clientX + "px";
				}
			}
			if (clicked.onTopEdge) {
				var currentHeight = Math.max(clicked.cy - e.clientY + clicked.h, minHeight);
				if (currentHeight > minHeight) {
					pane.style.height = currentHeight + "px";
					pane.style.top = e.clientY + "px";
				}
			}

			hintHide();
			return;
		}

		if (clicked &amp;&amp; clicked.isMoving) {

			var bounds = getBounds()

			// Set bounds of ghost pane
			if (bounds.length) {
				bounds.unshift(ghostpane)
				setBounds.apply(this, bounds);
				ghostpane.style.opacity = 0.2;
			} else {
				hintHide();
			}

			// Unsnap with drag, set bounds to preSnap bounds
			if (preSnap) {
				preSnap.draged = true
				setBounds(pane,
					e.clientX - preSnap.width / 2,
					e.clientY - Math.min(clicked.y, preSnap.height),
					preSnap.width,
					preSnap.height
				);
				return;
			}

			// moving
			pane.style.top = (e.clientY - clicked.y) + 'px';
			pane.style.left = (e.clientX - clicked.x) + 'px';

			return;
		}

		// This code executes when mouse moves without clicking

		// style cursor
        var db = document.documentElement || document.body
		if (onRightEdge &amp;&amp; onBottomEdge || onLeftEdge &amp;&amp; onTopEdge) {
			db.style.cursor = 'nwse-resize';
		} else if (onRightEdge &amp;&amp; onTopEdge || onBottomEdge &amp;&amp; onLeftEdge) {
			db.style.cursor = 'nesw-resize';
		} else if (onRightEdge || onLeftEdge) {
			db.style.cursor = 'ew-resize';
		} else if (onBottomEdge || onTopEdge) {
			db.style.cursor = 'ns-resize';
		} else if (canMove()) {
			db.style.cursor = 'move';
		} else {
			db.style.cursor = 'default';
		}
	}

	animate();

	function onUp(e) {
		calc(e);

		if (clicked &amp;&amp; clicked.isMoving) {
			// Check for Snap
			var bounds = getBounds()
			// Snap to bounds
			if (bounds.length) {
				// new snap: save preSnap size &amp; bounds then set bounds
				preSnap = {width: b.width, height: b.height};
				bounds.unshift(pane)
				preSnap.to = bounds
				setBounds.apply(this, preSnap.to);

			// Clicked: reduce size and destroy the preSnap state
			} else if (preSnap &amp;&amp; !preSnap.draged) {

				var o = RESIZE_MARGIN_INNER
				preSnap.to[1] += o
				preSnap.to[2] += o
				preSnap.to[3] -= o * 2
				preSnap.to[4] -= o * 2
				setBounds.apply(this, preSnap.to);
				preSnap = null;

			// Was dragged: just destroy the preSnap state
			} else {
				preSnap = null;
			}
			hintHide();
		} else if(clicked &amp;&amp; clicked.isResizing){
			// set bounds after resize to make sure they are % as required
			setBounds(pane)
		}



		clicked = null;
	}

	// Tripple click to center (comes in handy!)
	var clicks = [0, 0, 0]

	pane.addEventListener('click', trippleClick)

	function trippleClick(e) {
		clicks[2] = clicks[0] &amp;&amp; clicks[1] &amp;&amp; !clicks[2] ? e.timeStamp : clicks[2]
		clicks[1] = clicks[0] &amp;&amp; !clicks[1] ? e.timeStamp : clicks[1]
		clicks[0] = !clicks[0] ? e.timeStamp : clicks[0]

		var dif = clicks[2] - clicks[0]
		if (clicks[2] &amp;&amp; dif &lt; 400)
			center()
		if (!clicks[2] &amp;&amp; !clicks[3])
			setTimeout(function () {
				clicks = [0, 0, 0]
			}, 500)
		e.preventDefault()
		e.stopPropagation()
		return false
	}

	/**
	 * Center the pane in the browser window
	 */
	function center() {
		var w = window
		var pw = w.innerWidth * .75
		var ph = w.innerHeight * .75
		setBounds(pane, (w.innerWidth / 2) - (pw / 2), (w.innerHeight / 2) - (ph / 2), pw, ph);
	}

	// utility functions
	function hasHTMLElement(a) {
		for (var i in a) if (i instanceof HTMLElement) return true
	}

	function bindEvents(ele, events, callback) {
		events = events.split(' ')
		for (var e in events) ele.addEventListener(events[e], callback)
	}

	return {
		togglePercent: togglePercent,
		snapFullScreen: snapFullScreen,
		restorePreSnap:restorePreSnap,
		center: center
	}
}
return {makeDRS:makeDRS}
}()
</code></pre>
        </article>
    </section>




</div>

<nav>
    <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="core.util.DRS.makeDRS.html">makeDRS</a></li></ul><h3>Namespaces</h3><ul><li><a href="core.html">core</a></li><li><a href="core.util.html">util</a></li><li><a href="core.util.DRS.html">DRS</a></li></ul>
</nav>

<br class="clear">

<footer>
    Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Thu Sep 08 2016 21:28:13 GMT-0400 (EDT)
</footer>

<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>
